<?php
/* --------------------------------------------------------------
   ErrorHandler.php 2011-03-13 gambio
   Gambio GmbH
   http://www.gambio.de
   Copyright (c) 2011 Gambio GmbH
   Released under the GNU General Public License (Version 2)
   [http://www.gnu.org/licenses/gpl-2.0.html]
   --------------------------------------------------------------
*/

class ErrorHandler 
{
	var $errorstack = array();// Errorstack

	function ErrorHandler()
	{
		# display php errors
		ini_set('display_errors', '1');

		# intercepts E_NOTICE in older php versions (<5.3)
		if(defined('E_DEPRECATED') == false)
		{
			define('E_DEPRECATED', 0);
			define('E_USER_DEPRECATED', 0);
		}

		# intercepts E_NOTICE in older php versions (<5)
		if(defined('E_STRICT') == false)
		{
			define('E_STRICT', 0);
		}
		
		if(defined('MODULE_GAMBIO_ERROR_REPORT_EMAIL')) # use custom error_reporting level
		{
			if(MODULE_GAMBIO_ERROR_REPORT_HIDE_E_ERROR == 'True') 				$E_ERROR = E_ERROR; else $E_ERROR = 0;
			if(MODULE_GAMBIO_ERROR_REPORT_HIDE_E_WARNING == 'True') 			$E_WARNING = E_WARNING; else $E_WARNING = 0;
			if(MODULE_GAMBIO_ERROR_REPORT_HIDE_E_PARSE == 'True') 				$E_PARSE = E_PARSE; else $E_PARSE = 0;
			if(MODULE_GAMBIO_ERROR_REPORT_HIDE_E_NOTICE == 'True') 				$E_NOTICE = E_NOTICE; else $E_NOTICE = 0;
			if(MODULE_GAMBIO_ERROR_REPORT_HIDE_E_CORE_ERROR == 'True') 			$E_CORE_ERROR = E_CORE_ERROR; else $E_CORE_ERROR = 0;
			if(MODULE_GAMBIO_ERROR_REPORT_HIDE_E_CORE_WARNING == 'True') 		$E_CORE_WARNING = E_CORE_WARNING; else $E_CORE_WARNING = 0;
			if(MODULE_GAMBIO_ERROR_REPORT_HIDE_E_COMPILE_ERROR == 'True') 		$E_COMPILE_ERROR = E_COMPILE_ERROR; else $E_COMPILE_ERROR = 0;
			if(MODULE_GAMBIO_ERROR_REPORT_HIDE_E_COMPILE_WARNING == 'True')		$E_COMPILE_WARNING = E_COMPILE_WARNING; else $E_COMPILE_WARNING = 0;
			if(MODULE_GAMBIO_ERROR_REPORT_HIDE_E_USER_ERROR == 'True') 			$E_USER_ERROR = E_USER_ERROR; else $E_USER_ERROR = 0;
			if(MODULE_GAMBIO_ERROR_REPORT_HIDE_E_USER_WARNING == 'True') 		$E_USER_WARNING = E_USER_WARNING; else $E_USER_WARNING = 0;
			if(MODULE_GAMBIO_ERROR_REPORT_HIDE_E_USER_NOTICE == 'True') 		$E_USER_NOTICE = E_USER_NOTICE; else $E_USER_NOTICE = 0;
			if(MODULE_GAMBIO_ERROR_REPORT_HIDE_E_ALL == 'True') 				$E_ALL = E_ALL; else $E_ALL = 0;
			if(MODULE_GAMBIO_ERROR_REPORT_HIDE_E_STRICT == 'True') 				$E_STRICT = E_STRICT; else $E_STRICT = 0;
			if(MODULE_GAMBIO_ERROR_REPORT_HIDE_E_RECOVERABLE_ERROR == 'True') 	$E_RECOVERABLE_ERROR = E_RECOVERABLE_ERROR; else $E_RECOVERABLE_ERROR = 0;
			if(MODULE_GAMBIO_ERROR_REPORT_HIDE_E_DEPRECATED == 'True') 			$E_DEPRECATED = E_DEPRECATED; else $E_DEPRECATED = 0;
			if(MODULE_GAMBIO_ERROR_REPORT_HIDE_E_USER_DEPRECATED == 'True') 	$E_USER_DEPRECATED = E_USER_DEPRECATED; else $E_USER_DEPRECATED = 0;
						
			error_reporting(
				E_ALL 
				& ~$E_ERROR 
				& ~$E_WARNING 
				& ~$E_PARSE 
				& ~$E_NOTICE 
				& ~$E_CORE_ERROR 
				& ~$E_CORE_WARNING 
				& ~$E_COMPILE_ERROR 
				& ~$E_COMPILE_WARNING 
				& ~$E_USER_ERROR 
				& ~$E_USER_WARNING 
				& ~$E_USER_NOTICE 
				& ~$E_ALL 
				& ~$E_STRICT 
				& ~$E_RECOVERABLE_ERROR 
				& ~$E_DEPRECATED 
				& ~$E_USER_DEPRECATED
			);
		}
		else # use default error_reporting level
		{
			error_reporting(
				E_ALL
				& ~E_NOTICE 
				& ~E_DEPRECATED 
				& ~E_STRICT
			);			
		}
	}
	
	/*
	* @desc: Error handling function
	*/
	function HandleError($errno, $errstr, $errfile, $errline, $errcontext) 
	{
		# should we show this error code?
		if((error_reporting() & $errno) == false) return true;
		
		# backtrace
		$backtrace = $this->HandleBacktrace(debug_backtrace());
		
		switch($errno) 
		{
			case E_USER_ERROR:
			case E_ERROR: // can't be catched by version 5.2
			case E_PARSE: // can't be catched by version 5.2
			case E_CORE_ERROR: // can't be catched by version 5.2
			case E_COMPILE_ERROR: // can't be catched by version 5.2
			case E_RECOVERABLE_ERROR:
				$this->AddErrorToStack('FATAL ERROR('.$errno.')', $errstr, $errfile, $errline, $errcontext, $backtrace);
				$this->do_log();
				die();
				break;
			
			case E_COMPILE_WARNING:// can't be catched by version 5.2
			case E_WARNING:
			case E_USER_WARNING:
			case E_CORE_WARNING:
				$this->AddErrorToStack('WARNING('.$errno.')', $errstr, $errfile, $errline, $errcontext, $backtrace);
				$this->do_log();
				break;
			
			case E_STRICT: // can't be catched by version 5.2
				$this->AddErrorToStack('STRICT('.$errno.')', $errstr, $errfile, $errline, $errcontext, $backtrace);
				$this->do_log();
				break;
			
			# Available in php 5.3
			case E_USER_DEPRECATED:
			case E_DEPRECATED:
				$this->AddErrorToStack('DEPRICATED('.$errno.')', $errstr, $errfile, $errline, $errcontext, $backtrace);
				$this->do_log();
				break;
			
			case E_USER_NOTICE:
			case E_NOTICE:
				$this->AddErrorToStack('NOTICE('.$errno.')', $errstr, $errfile, $errline, $errcontext, $backtrace);
				$this->do_log();
				break;
			
			default:
				$this->AddErrorToStack('UNKNOWN ERROR('.$errno.')', $errstr, $errfile, $errline, $errcontext, $backtrace);
				$this->do_log();
				break;
		}
		return true; // don't execute php internal error handler
	}
	
	function do_log()
	{
		if (count($this->errorstack) > 0) 
		{
			$this->LogErrorsTo('file');
			$this->LogErrorsTo('screen');
			$this->SendErrors();
			
			#reset stack after logging
			$this->errorstack = array();
		}		
	}
	
	### SUBFUNCTIONS ###
	
	/*
	* @desc: Pushes an error on the error stack
	* @param: (str)$level - a process generated error level
	* @param: (str)$msg - the Error message
	*/
	function AddErrorToStack($level,$errstr,$errfile,$errline,$errcontext,$backtrace) {		
		$error = array(
					"level" 	=> $level,
					"message" 	=> $errstr,
					"errfile" 	=> $errfile,
					"errline" 	=> $errline,
					"backtrace" => $backtrace,
					"context" 	=> $errcontext
				);
		array_push($this->errorstack, $error);
	}
	
	/*
	* @desc: Logs the whole stack to given $logtype
	*/
	function LogErrorsTo($logtype) {
		$splitline = str_repeat('=',80);
		//$date = date('Y-m-d H:i');
		
		switch ($logtype) {
			case 'file':
				$t_error_string  = $splitline."\n";
				$t_error_string .= $this->ErrorOutput('file');
				
				$coo_error_log = new FileLog('errors');
				$coo_error_log->write($t_error_string);
			break;
			
			case 'mysql':
			break;
			
			case 'screen':
				echo '<pre>'.$this->ErrorOutput('screen').'</pre>';
			break;
			
			default:
			break;
		}
	}
	
	/*
	* @desc: returns Human readable output data
	*/
	function ErrorOutput($p_target='screen') 
	{
		$error_data = "";
		$t_div_id = rand(1000, 9000);
		
		foreach ($this->errorstack as $error) 
		{
			switch($p_target)
			{
				case 'screen':
					
					$error_data .= '<div style="background-color: #ff9797">'. $error['level'] .': "'. $error['message'] .'" in '. $error['errfile'] .':'. $error['errline'] .' ';
					$error_data .= "(<a href=\"#\" onclick=\"document.getElementById('error_".$t_div_id."').style.display='block';return false;\">Details</a>)</div>";
					
					$error_data .= '<div id="error_'.$t_div_id.'" style="background-color:#ffcaca; display:none">';
					$error_data .= "Backtrace:\n". $error['backtrace'];
					
					if(defined('MODULE_GAMBIO_ERROR_REPORT_DETAILS_SCREEN'))
					{
						if(MODULE_GAMBIO_ERROR_REPORT_DETAILS_SCREEN == 'True') 
						{
							$error_data .= "Context: ". print_r($error['context'],true) . "\n\n";
							$error_data .= "Session: ". (!empty($_SESSION) ? print_r($_SESSION,true) : 'No Session');
						}
					}
					$error_data .= '</div>';
					break;
					
				case 'file':
					$error_data .= 
						date('Y-m-j H-i-s') .' ('. $_SERVER['REMOTE_ADDR']. ")\n".	
						$error['level'] .': '. $error['message'] ."\n".
						"  in ". $error['errfile'] .':'. $error['errline']	. "\n".
						"Backtrace: \n"	. $error['backtrace'] ."\n";
						
					if(defined('MODULE_GAMBIO_ERROR_REPORT_DETAILS_FILE'))
					{
						if(MODULE_GAMBIO_ERROR_REPORT_DETAILS_FILE == 'True') 
						{
							$error_data .= "Context: ". print_r($error['context'],true) . "\n\n";
							$error_data .= "Session: ". (!empty($_SESSION) ? print_r($_SESSION,true) : 'No Session') . "\n\n";
						}
					}
					break;
					
				case 'mail':
					$error_data .= 
						date('Y-m-j H-i-s') .' ('. $_SERVER['REMOTE_ADDR']. ")\n".	
						"Shop URL: ". HTTP_SERVER.DIR_WS_CATALOG . "\n\n".
						"Level: "	. $error['level']		. "\n".
						"Message: "	. $error['message']		. "\n".
						"  in ".		  $error['errfile'] .':'. $error['errline']	. "\n".
						"Backtrace: \n"	. $error['backtrace']	. "\n";
						
					if(defined('MODULE_GAMBIO_ERROR_REPORT_DETAILS_MAIL'))
					{
						if(MODULE_GAMBIO_ERROR_REPORT_DETAILS_MAIL == 'True') 
						{
							$error_data .= "Context: ". print_r($error['context'],true) . "\n\n";
							$error_data .= "Session: ". (!empty($_SESSION) ? print_r($_SESSION,true) : 'No Session') . "\n\n";
						}
					}
					break;
			}
			
		}
		return $error_data;
	}
	
	/*
	* @desc: Handle backtrace
	*/
	function HandleBacktrace($backtrace) {
		array_shift($backtrace);
		$run = 0;
		$backtracelines = "";
		foreach ($backtrace as $data) {
			
			if (empty($data['file'])) $data['file'] = 'unknown';
			if (empty($data['line'])) $data['line'] = 0;

			if(!empty($data['class'])) {
				$backtracelines .= '#'.$run.'  (#'.$data['class'].') '.$data['function'].' called at ['.$data['file'].':'.$data['line'].']';
			}
			else {
				$backtracelines .= '#'.$run.'  '.$data['function'].' called at ['.$data['file'].':'.$data['line'].']';
			}
			
			$backtracelines .= "\n";
			
			$run ++;
		}
		
		return $backtracelines;
	}
	
	/*
	* @desc: send actual errorstack via mail
	*/
	function SendErrors() 
	{
		if(defined('MODULE_GAMBIO_ERROR_REPORT_EMAIL')) 
		{
			if(MODULE_GAMBIO_ERROR_REPORT_EMAIL != '') 
			{
				$t_to 		= MODULE_GAMBIO_ERROR_REPORT_EMAIL;
				$t_from 	= MODULE_GAMBIO_ERROR_REPORT_EMAIL;
				$t_subject 	= 'Debug Report '.HTTP_SERVER.DIR_WS_CATALOG;
				$t_body 	= $this->ErrorOutput('mail');
				
				$this->send_mail($t_to, $t_from, $t_subject, $t_body);
			}
		}
	}
	
	function send_mail($emailTo, $emailFrom, $subject, $body)
	{
		mail($emailTo, 
				 $subject, 
				 $body,
				 'From: '. $emailFrom ."\n".
				 'Content-Type: text/plain'
				);
		return true;
	}
}
?>